/*
 * 智能语言控制控制，支持天猫、小爱、小度、google Assistent同时控制
 * 2022-08-22
 * QQ交流群：566565915
 * 官网https://bemfa.com
 */

#define server_ip "bemfa.com" // 巴法云服务器地址默认即可
#define server_port "8344"    // 服务器端口，tcp创客云端口8344

//********************需要修改的部分*******************//

#define wifi_name "Sensen"       // WIFI名称，区分大小写，不要写错
// #define wifi_name "15367660966"  // WIFI名称，区分大小写，不要写错
#define wifi_password "15367660" // WIFI密码

String UID = "4892bd7fe005ecbbccf35929157ec7e7"; // 用户私钥，可在控制台获取,修改为自己的UID
String aptype = "006";                           // 001插座类型，002灯类型，003风扇类型，004传感器，005空调，006开关，009窗帘
String TOPIC = "";                               // 主题名字，可在控制台新建
String k1 = "on";
String k2 = "on";
String k3 = "on";
String k4 = "on";
String k0 = "on";
//**************************************************//
// 最大字节数
#define MAX_PACKETSIZE 512
// 设置心跳值60s
#define KEEPALIVEATIME 2 * 1000
// tcp客户端相关初始化，默认即可
int failCount = 0; // 定义失败连接次数
WiFiClient TCPclient;
String TcpClient_Buff = ""; // 初始化字符串，用于接收服务器发来的数据
unsigned int TcpClient_BuffIndex = 0;
unsigned long TcpClient_preTick = 0;
unsigned long preHeartTick = 0;    // 心跳
unsigned long preTCPStartTick = 0; // 连接
bool preTCPConnected = false;

void startSTA(String name, String password);
void doTCPClientTick();
void startTCPClient();
void sendtoTCPServer(String p);
void delayRestart(float t);
void restoreFactory();
void yourFunction();
// bool processFile(String targetValue, char timeData[], char parameterData[]);
// void operateOnDataInFile(const char *filename, String targetChar, String action, int &newData);
/**
 * 固件升级函数
 * 在需要升级的地方，加上这个函数即可，例如setup中加的updateBin();
 * 原理：通过http请求获取远程固件，实现升级
 */

// 当升级开始时，打印日志
void update_started()
{
  Serial.println("CALLBACK:  HTTP update process started");
  update = true;
  // digitalWrite(spk_PIN, HIGH); // 指示灯引脚
}

// 当升级结束时，打印日志
void update_finished()
{
  Serial.println("CALLBACK:  HTTP update process finished");
}

// 当升级中，打印日志
void update_progress(int cur, int total)
{
  static bool update = false;
  int bfb = map(cur, 0, total, 0, 100);
  digitalWrite(spk_PIN, !digitalRead(spk_PIN)); // 指示灯引脚
  if (bfb % 5 == 0)
  {
    if (!update)
    {
      if (bfb == 100)
      {
        delay(1000);
      }
      sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC + "&msg=" + TOPIC + "#" + String(bfb) + " % \r\n");
      update = true;
      Serial.printf("CALLBACK:  HTTP update process at %d of %d bytes... %d %\n", cur, total, bfb);
    }
  }
  else
  {
    update = false;
  }
}

// 当升级失败时，打印日志
void update_error(int err)
{
  Serial.printf("CALLBACK:  HTTP update fatal error code %d\n", err);
}

// 升级主程序函数
void updateBin()
{
  String upUrl = "http://bin.bemfa.com/b/3BcNDg5MmJkN2ZlMDA1ZWNiYmNjZjM1OTI5MTU3ZWM3ZTc=" + TOPIC + ".bin"; // OTA固件链接，请替换为自己的固件链接，如果接收到msg=update，开始执行固件升级
  Serial.println(ESP.getFreeHeap());
  Serial.println("开始升级");
  WiFiClient UpdateClient;
  httpUpdate.onStart(update_started);     // 当升级开始时
  httpUpdate.onEnd(update_finished);      // 当升级结束时
  httpUpdate.onProgress(update_progress); // 当升级中
  httpUpdate.onError(update_error);       // 当升级失败时

  t_httpUpdate_return ret = httpUpdate.update(UpdateClient, upUrl);
  switch (ret)
  {
  case HTTP_UPDATE_FAILED: // 当升级失败
    Serial.println("当升级失败");
    vTaskDelete(NULL);
    break;
  case HTTP_UPDATE_NO_UPDATES: // 当无升级
    Serial.println("当无升级");
    break;
  case HTTP_UPDATE_OK: // 当升级成功
    Serial.println("当升级成功");
    break;
  }
}

/*
  网络连接
 */
void startSTA(String name, String password)
{
  Serial.println("连接网络......");
  WiFi.disconnect();          // 断开连接
  WiFi.mode(WIFI_STA);        // STA模式
  WiFi.begin(name, password); // 连接路由器
}

/*
  WiFiTick
  检查是否需要初始化WiFi
  检查WiFi是否连接上，若连接成功启动TCP Client
  控制指示灯
*/
void doWiFiTick()
{
  static bool taskStarted = false;
  static uint32_t lastWiFiCheckTick = 0;

  if (WiFi.status() != WL_CONNECTED) // 检查是否连接成功
  {
    if (millis() - lastWiFiCheckTick > 60000) // 未连接重连
    {
      for (size_t i = 0; i < 4; i++)
      {
        digitalWrite(spk_PIN, !digitalRead(spk_PIN)); // 指示灯引脚
        delay(100);
      }
      startSTA("Sensen", "15367660");
      lastWiFiCheckTick = millis();
    }
    taskStarted = false;
  }
  else if (taskStarted == false)
  {
    digitalWrite(spk_PIN, HIGH); // 指示灯引脚
    delay(100);
    digitalWrite(spk_PIN, LOW); // 指示灯引脚
    Serial.println("网络连接成功");
    Serial.print("IP地址:");
    Serial.println(WiFi.localIP());
    taskStarted = true;
    startTCPClient();
  }
}

//  微信消息推送函数
void doHttpStick(String msg)
{
  String type = "2"; // 1表示是预警消息，2表示设备提醒消息
  WiFiClient WiFi;
  HTTPClient http;
  String ApiUrl = "http://api.bemfa.com/api/wechat/v1/"; // 默认 api 网址
  String postData = "uid=" + UID + "&type=" + type + "&device=" + "太阳能控制器" + "&msg=" + msg;
  http.begin(WiFi, ApiUrl);                                            // Specify request destination
  http.addHeader("Content-Type", "application/x-www-form-urlencoded"); // Specify content-type header
  http.POST(postData);                                                 // Send the request
  http.end();                                                          // Close connection
}

// 函数用于将多个变量相加后判断是否发生变化
bool check_and_convert_sum_change(int var1, int var2, int var3)
{
  static int prev = 0; // 静态变量
  int sum = var1 + var2 + var3;
  bool has = prev != sum;
  prev = sum;
  return has;
}
void sendtoTCPServer()
{
  sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=" + (in ? "on" : "off") +
                  "#" + ble_U + "#" + ble_I + "#" + ble_U * ble_I + "#" + int(ble_dl) + "#" + ble_T1 +
                  "#" + ble_T2 + "#" + ble_U1 + "#" + ble_U2 + "#" + ble_U3 + "#" + ble_U4 +
                  "#" + xdc_U / 1000 + "#" + xdc_I / 1000 + "#" + Q + "#" + MOS_T +
                  "#" + tyn_U / 1000 + "#" + tyn_I / 1000 + "#" + tyn_P + "#" + kWh + "#" + Duty +
                  "#" + (in ? out : false) + "#" + (in ? mppt : false) + "#" + ble + "#" + rebootReason + "\r\n"); // 推送消息
}
/*
 *发送数据到TCP服务器
 */
void sendtoTCPServer(String p)
{
  if (!TCPclient.print(p))
  {
    Serial.println("TCPclien发送失败");
    TCPclient.stop();
  }
  preHeartTick = millis(); // 心跳计时开始，需要每隔60秒发送一次数据
}
/*
 *初始化和服务器建立连接
 */
void startTCPClient()
{
  Serial.println("TCPclien开始连接");
  static bool client = false;

  if (TCPclient.connect(server_ip, atoi(server_port)))
  {
    String mac = WiFi.macAddress().substring(8);               // 取mac地址做主题用
    mac.replace(":", "");                                      // 去掉:号
    TOPIC = mac + aptype;                                      // 主题添加类型
    String tcpTemp = "";                                       // 初始化字符串
    tcpTemp = "cmd=1&uid=" + UID + "&topic=" + TOPIC + "\r\n"; // 构建订阅指令
    Serial.print("发送订阅指令:" + tcpTemp);
    sendtoTCPServer(tcpTemp); // 发送订阅指令
    tcpTemp = "";             // 清空
    /*
     //如果需要订阅多个主题，可发送  cmd=1&uid=xxxxxxxxxxxxxxxxxxxxxxx&topic=xxx1,xxx2,xxx3,xxx4\r\n
    教程：https://bbs.bemfa.com/64
     */
    TCPclient.setNoDelay(true);
    if (!client)
    {
      client = true;
      // 判断是否意外重启，发送重启原因
      if (rebootReason != "")
      {
        sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=异常重启=" + rebootReason + "\r\n"); // 推送消息
      }
      sendtoTCPServer();
    }
    Serial.println("TCPclien连接成功");
  }
  else
  {
    failCount = failCount + 1;
    if (failCount > 10) // 如果失败连接10次
    {
      failCount = 0;
      WiFi.disconnect(); // 断开连接
    }
    Serial.println();
    Serial.println("TCPclien连接失败");
    TCPclient.stop();
  }
  preTCPStartTick = millis();
}
/*
 *接收数据，发送数据
 */
void doTCPClientTick()
{
  if (WiFi.status() != WL_CONNECTED)
  {
    preTCPStartTick = millis();
    return;
  }
  if (!TCPclient.connected()) // 断开重连
  {
    if (millis() - preTCPStartTick > 3000) // 重新连接
    {
      startTCPClient();
    }
  }
  else
  {
    while (TCPclient.available() && TcpClient_BuffIndex <= MAX_PACKETSIZE)
    {
      // 收数据
      char c = TCPclient.read();
      TcpClient_Buff += c;
      TcpClient_BuffIndex++;
    }
    if (millis() - preHeartTick >= KEEPALIVEATIME)
    {
      // 保持心跳
      Serial.println("发送心跳");
      sendtoTCPServer("cmd=0&msg=keep\r\n");
      if (check_and_convert_sum_change(tyn_I / 100, xdc_I / 100, ble_I * 10))
      {
        sendtoTCPServer();
      }
    }
    if (TcpClient_Buff.length() >= 1)
    {
      TCPclient.flush();
      if (TcpClient_Buff.length() >= 60)
      {
        Serial.println("接收数据:/n" + String(TcpClient_Buff.length()) + "字节" + TcpClient_Buff);
        if (TcpClient_Buff.indexOf("f5") > 0)
        {
          sendtoTCPServer();
        }
        else if (TcpClient_Buff.indexOf("update") > 0)
        {
          Serial.println("升级");
          digitalWrite(2, LOW);
          digitalWrite(45, LOW);
          updateBin();
        }
        else if (TcpClient_Buff.indexOf("delete") > 0)
        {
          File root = SPIFFS.open("/");
          if (!root)
          {
            sendtoTCPServer("cmd=2&uid=" + UID +
                            "&topic=" + TOPIC +
                            "/up&msg=打开失败\r\n");
          }
          int a = TcpClient_Buff.indexOf("delete:");
          int b = TcpClient_Buff.indexOf("\n", a);
          if (a > 0 && b > 0)
          {
            String VaLue = TcpClient_Buff.substring(a + 7, b);
            if (SPIFFS.remove("/" + VaLue + ""))
            {

              Serial.println(" remove sucess");
              sendtoTCPServer("cmd=2&uid=" + UID +
                              "&topic=" + TOPIC +
                              "/up&msg=remove sucess\r\n");
            }
          }
          for (size_t i = 0; File file = root.openNextFile(); i++)
          {
            sendtoTCPServer("cmd=2&uid=" + UID +
                            "&topic=" + TOPIC +
                            "/up&msg=" + file.name() + "#" + file.size() + "\r\n"); // 推送消息
            while (file.available())
            {
              String content = "";
              int length = 127;
              content = file.readStringUntil('\n');
              for (size_t i = 0; i < content.length(); i += length)
              {
                sendtoTCPServer("cmd=2&uid=" + UID +
                                "&topic=" + TOPIC +
                                "/up&msg=" + content.substring(i, min(i + length, content.length())) + "\r\n");
              }
              delay(500);
            }
            delay(500);
            file.close();
          }
          // File file = SPIFFS.open("/charts.txt", "r");
          // if (file)
          // {
          //   const size_t bytesToKeep = 217; // 假设要保留前100字节
          //   char buffer[bytesToKeep];
          //   size_t bytesRead = file.read((uint8_t *)buffer, bytesToKeep);
          //   file.close();
          //   // 后续步骤
          //   File file = SPIFFS.open("/charts.txt", "w");
          //   if (file)
          //   {
          //     file.write((uint8_t *)buffer, bytesRead);
          //     file.close();
          //   }
          // }
        }
        else if (TcpClient_Buff.indexOf("charts") > 0) // 把图表数据发送出去
        {
          if (TcpClient_Buff.indexOf("Resetcharts") > 0) // 清空数据
          {
            Serial.println("重置图表");
            // 清空文件
            File file = SPIFFS.open("/charts.txt", "w");
            file.close();
            sendtoTCPServer("cmd=2&uid=" + UID +
                            "&topic=" + TOPIC +
                            "/up&msg=重置图表\r\n");
          }
          else
          {
            int a = TcpClient_Buff.indexOf("charts") + 6;
            int b = a;
            char ch[] = ".";
            while (TcpClient_Buff.length() && (isdigit(TcpClient_Buff.charAt(b)) || TcpClient_Buff.charAt(b) == ch[0]))
            {
              b++;
            }
            if (a > 0 && b > a)
            {
              yourFunction();
              String VaLue = TcpClient_Buff.substring(a, b);
              char parameter[125]; // 假设最多存储 100 组参数数据
              if (processFile(VaLue, parameter))
              {
                Serial.printf("找到数据%s", parameter);
                Serial.println();
                sendtoTCPServer("cmd=2&uid=" + UID +
                                "&topic=" + TOPIC +
                                "/up&msg=M" + parameter + "\r\n"); // 推送消息
              }
              else
              {
                Serial.println("没有找到数据");
                sendtoTCPServer("cmd=2&uid=" + UID +
                                "&topic=" + TOPIC +
                                "/up&msg=没有找到数据\r\n"); // 推送消息
              }
            }
          }
        }
        else if (TcpClient_Buff.indexOf("out_on") > 0) // 如果是消息==打开
        {
          Serial.println("充电开");
          out = true;
          preHeartTick = 0;
        }
        else if (TcpClient_Buff.indexOf("out_off") > 0)
        {
          Serial.println("充电关");
          out = false;
          preHeartTick = 0; // 心跳计时清零，发送一次数据
        }
        else if (TcpClient_Buff.indexOf("mppt_on") > 0)
        {
          Serial.println("智能充电开");
          mppt = true;
          preHeartTick = 0; // 心跳计时清零，发送一次数据
        }
        else if (TcpClient_Buff.indexOf("mppt_off") > 0)
        {
          Serial.println("智能充电关");
          mppt = false;
          preHeartTick = 0; // 心跳计时清零，发送一次数据
        }
        else if (TcpClient_Buff.indexOf("ble_off") > 0)
        {
          ble = false;
          preHeartTick = 0;
        }
        else if (TcpClient_Buff.indexOf("ble_on") > 0)
        {
          ble = true;
          preHeartTick = 0;
        }
        else if (TcpClient_Buff.indexOf("Reboot") > 0)
        {
          Serial.println("重启");
          delayRestart(1);
        }
        else if (TcpClient_Buff.indexOf("Reset") > 0) // 清空数据
        {
          Serial.println("重置电量");
          SPIFFS.format();
          sendtoTCPServer(
              "cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=重置电量\r\n"); // 推送消息
        }
        else
        {
          int a = TcpClient_Buff.indexOf("msg=") + 4;
          int b = TcpClient_Buff.indexOf("=", a);
          int c = TcpClient_Buff.indexOf("\n", a);
          String d = "";
          float e = 0;
          if (a < b && b < c)
          {
            d = TcpClient_Buff.substring(a, b);
            e = atof(TcpClient_Buff.substring(b + 1, c).c_str()) * 1000;
            Serial.println("" + d + "=" + e + "");
            if (d.indexOf("Uxm") >= 0)
            {
              config.Uxm = Ux = Uxm = e;
            }
            else if (d.indexOf("Uxf") >= 0)
            {
              config.Uxf = Uxf = e;
            }
            else if (d.indexOf("It") >= 0)
            {
              config.It = It = e;
            }
            else if (d.indexOf("Utg") >= 0)
            {
              Utg = e;
            }
            else if (d.indexOf("T") >= 0)
            {
              config.T = T = e / 1000;
            }
            saveConfig();
          }
          if (TcpClient_Buff.indexOf("canshu") >= 0 || (a < b && b < c))
          {
            sendtoTCPServer(
                "cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=canshu#" +
                Uxm / 1000 + "#" + It / 1000 + "#" + Uxf / 1000 + "#" +
                Utg / 1000 + "#" + T + "#\r\n"); // 推送消息
          }
        }
        delay(2000);
        sendtoTCPServer();
      }
    }
    TcpClient_Buff = "";
    TcpClient_BuffIndex = 0;
  }
}

/*
 *启动巴法云
 */
void qidongBFY()
{
  doWiFiTick();
  doTCPClientTick();
}
